﻿using System;
using System.Collections;
using System.Collections.Concurrent;
using SFML.Graphics;
using SFML.Window;
using Transformable_Engine_v2.Engine.GameObjects.Containers;
using Transformable_Engine_v2.Engine.GameObjects.Primitives;
using Transformable_Engine_v2.Engine.Global;
using Transformable_Engine_v2.Engine.Graphics;
using Transformable_Engine_v2.Engine.Input;
using Transformable_Engine_v2.Engine.Rendering;
using Transformable_Engine_v2.Engine.Time;
using Transformable_Engine_v2.Engine.Transformable_Math.Geometry;

namespace Transformable_Engine_v2.Engine
{
    public static class Game
    {
        private static Container _implementer;
        private static RenderWindow _window;

        private static FrameTimer _frameTimer;
        private static FrameTimer _fixedFrameTimer;

        public static GameProperties Options { get; private set; }

        public static double Fps { get { return 1/_frameTimer.ElapsedGameTime.TotalSeconds; } }
        public static double UpdateFps { get { return 1 / _fixedFrameTimer.ElapsedGameTime.TotalSeconds; } }

        private static int _drawWaitingTime;

        private static ConcurrentQueue<Action> _events;

        public static void AddTimers(params Timer[] timer)
        {
            _implementer.AddTimers(timer);
        }

        public static void Start(Container implementer, GameProperties properties)
        {
            Console.WriteLine("{0} has started!", properties.Title);

            _drawWaitingTime = 30;

            _events = new ConcurrentQueue<Action>();

            _implementer = implementer;
            Options = properties;

            _frameTimer = new FrameTimer();
            _fixedFrameTimer = new FrameTimer();


            _window = new RenderWindow(Options.ScreenSize.ToGridbound().ToVideoMode(), Options.Title, (Styles)Options.Style);
            //_window.SetVerticalSyncEnabled(true);

            //Setup events
            _window.Closed += OnClosed;
            //_window.GainedFocus 

            if (Options.GamepadEnabled)
            {
                _window.JoystickButtonPressed += GamepadHandler.OnJoystickButtonPressed;
                _window.JoystickButtonReleased += GamepadHandler.OnJoystickButtonReleased;
                _window.JoystickConnected += GamepadHandler.OnJoystickConnected;
                _window.JoystickDisconnected += GamepadHandler.OnJoystickDisconnected;
                _window.JoystickMoved += GamepadHandler.OnJoystickMoved;
            }
            

            if(Options.KeyboardEnabled) {
                _window.KeyPressed += KeyboardHandler.OnKeyPress;
                _window.KeyReleased += KeyboardHandler.OnKeyReleased;
                _window.TextEntered += KeyboardHandler.OnTextEntered;
            }
            
            //_window.LostFocus
            if(Options.MouseEnabled) 
            {
                _window.MouseButtonPressed += MouseHandler.OnClick;
                _window.MouseButtonReleased += MouseHandler.OnRelease;
                //_window.MouseEntered
                //_window.MouseLeft
                //_window.MouseMoved
                //_window.MouseWheelMoved
            }

            //_window.Resized

            //Initialize handlers
            RenderTargetHandler.Initialize(_window);
            ViewHandler.Initialize(_window);
            ShaderHandler.Initialize();
            SpriteSheet.Initialize();

            //Setup Icon
            if (Options.IconPath != null)
            {
                var img = new Image(Options.IconPath);
                _window.SetIcon(img.Size.X, img.Size.Y, img.Pixels);
            }

            if(Options.ShowGrid)
                _implementer.AddChildren(new GridPrimitive(new TE_Rectangle(0, 0, Options.ScreenSize), 1, -Options.BackgroundColor));

            _implementer.LoadContent();
            _implementer.Initialize();

            //Start drawing
            new System.Threading.Thread(UpdateThread).Start();

            _frameTimer.Start();

            while(_window.IsOpen()) 
            {
                //Draw at 30 fps
                _window.DispatchEvents();
                _window.Clear(Options.BackgroundColor.ToSFMLColor());

                _frameTimer.Update();
                _implementer.Update(_frameTimer.ElapsedGameTime);

                if(Options.MouseEnabled)
                    MouseHandler.Update(_frameTimer.ElapsedGameTime, _window);

                _implementer.Draw();
                _window.Display();

                if (_drawWaitingTime > 0)
                {
                    System.Threading.Thread.Sleep(_drawWaitingTime);
                    System.Threading.Thread.Yield();
                }

                if (Fps < Options.TargetFps && _drawWaitingTime > 0)
                {
                    _drawWaitingTime--;
                }
                else if (Fps > Options.TargetFps)
                {
                    _drawWaitingTime++;
                }
            }

            _implementer.UnloadContent();
            _implementer.Close();
        }

        static void UpdateThread()
        {
            _fixedFrameTimer.Start();

            while(_window.IsOpen()) 
            {
                _fixedFrameTimer.Update();
                _implementer.FixedStepUpdate(_fixedFrameTimer.ElapsedGameTime);

                Action e;
                while (_events.TryDequeue(out e))
                {
                    e();
                }

                System.Threading.Thread.Sleep(Options.UpdateSleepTime);
                System.Threading.Thread.Yield();
            }
        }

        /// <summary>
        /// Function called when the window is closed
        /// </summary>
        static void OnClosed(object sender, EventArgs e)
        {
            Close();
        }

        /// <summary>
        /// Method that closes the window
        /// </summary>
        public static void Close()
        {
            RemoveHandlers();
            _window.Close();
        }

        static void RemoveHandlers()
        {
            //Setup events
            _window.Closed -= OnClosed;
            //_window.GainedFocus 

            if (Options.GamepadEnabled)
            {
                _window.JoystickButtonPressed -= GamepadHandler.OnJoystickButtonPressed;
                _window.JoystickButtonReleased -= GamepadHandler.OnJoystickButtonReleased;
                _window.JoystickConnected -= GamepadHandler.OnJoystickConnected;
                _window.JoystickDisconnected -= GamepadHandler.OnJoystickDisconnected;
                _window.JoystickMoved -= GamepadHandler.OnJoystickMoved;
            }
            

            if(Options.KeyboardEnabled) {
                _window.KeyPressed -= KeyboardHandler.OnKeyPress;
                _window.KeyReleased -= KeyboardHandler.OnKeyReleased;
                _window.TextEntered -= KeyboardHandler.OnTextEntered;
            }
            
            //_window.LostFocus
            if(Options.MouseEnabled) 
            {
                _window.MouseButtonPressed -= MouseHandler.OnClick;
                _window.MouseButtonReleased -= MouseHandler.OnRelease;
                //_window.MouseEntered
                //_window.MouseLeft
                //_window.MouseMoved
                //_window.MouseWheelMoved
            }

            //_window.Resized
        }


        public static void AddToEventQueue(Action action)
        {
            _events.Enqueue(action);
        }
    }
}